package at.fhooe.mc.control;

import java.rmi.Naming;
import java.rmi.RemoteException;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JOptionPane;
import sqelevator.IElevator;
import at.fhooe.mc.control.exception.InvalidEnumValueException;
import at.fhooe.mc.control.exception.SameClockTickException;
import at.fhooe.mc.data.ControlMode;
import at.fhooe.mc.data.DirectionType;
import at.fhooe.mc.data.DoorState;
import at.fhooe.mc.data.ElevatorData;
import at.fhooe.mc.data.FloorData;
import at.fhooe.mc.data.SystemData;
import at.fhooe.mc.data.control.ElevatorControlData;

/**
 * abstraction layer for the elevator control center to the remote interface
 *
 * @author Thomas Wirth
 *
 */
public class RemoteElevatorAdapter implements IAdapter
{
	private static final int MAX_TRIES = 3;

	private IElevator controller;

	@Override
	public boolean init()
	{
		try
		{
			// Registry registry = LocateRegistry.getRegistry("localhost",
			// Registry.REGISTRY_PORT);
			// controller = (IElevator) registry.lookup("ElevatorSim");

			controller = (IElevator) Naming.lookup("rmi://localhost/ElevatorSim");

			return true;
		}
		catch (Exception e)
		{
			JOptionPane.showMessageDialog(null,
				"Unable to connect to Remote Elevator Device","Remote Error",JOptionPane.ERROR_MESSAGE);
			return false;
		}
	}

	@Override
	public SystemData parseData(SystemData oldInformation,
		List<ElevatorControlData> controlElevatorList) throws RemoteException,
		SameClockTickException, InvalidEnumValueException
	{
		SystemData systemData = null;
		int tries = 0;
		long currentClockTick = controller.getClockTick();

		if (oldInformation != null && currentClockTick == oldInformation.getClockTick())
		{
			throw new SameClockTickException();
		}

		do
		{
			systemData = new SystemData();

			systemData.setClockTick(currentClockTick);

			int numberOfElevator = controller.getElevatorNum();

			int numberOfFloors = controller.getFloorNum();

			systemData.setFloorHeight(controller.getFloorHeight());

			for (int floorIndex = 0; floorIndex < numberOfFloors; floorIndex++)
			{

				FloorData floor = new FloorData();
				floor.setFloorIndex(floorIndex);
				floor.setFloorButtonUp(controller.getFloorButtonUp(floorIndex));
				floor.setFloorButtonDown(controller.getFloorButtonDown(floorIndex));

				systemData.getFloors().add(floor);
			}

			for (int elevatorIndex = 0; elevatorIndex < numberOfElevator; elevatorIndex++)
			{

				ControlMode mode = null;
				if (controlElevatorList == null)
				{
					mode = ElevatorControlData.DEFAULT_MODE;
				}
				else
				{
					mode = controlElevatorList.get(elevatorIndex).getMode();
				}
				ElevatorData elevator = new ElevatorData(numberOfFloors, mode);
				elevator.setElevatorIndex(elevatorIndex);

				elevator.setElevatorFloor(controller.getElevatorFloor(elevatorIndex));
				elevator.setElevatorPosition(controller.getElevatorPosition(elevatorIndex));
				elevator.setCurrentTarget(controller.getTarget(elevatorIndex));
				elevator.setCommittedDirection(RemoteElevatorAdapter.convertDirectionType(controller.getCommittedDirection(elevatorIndex)));
				elevator.setElevatorDoorStatus(RemoteElevatorAdapter.convertDoorState(controller.getElevatorDoorStatus(elevatorIndex)));
				elevator.setElevatorSpeed(controller.getElevatorSpeed(elevatorIndex));
				elevator.setElevatorAccel(controller.getElevatorAccel(elevatorIndex));
				elevator.setElevatorCapacity(controller.getElevatorCapacity(elevatorIndex));
				elevator.setElevatorWeight(controller.getElevatorWeight(elevatorIndex));

				for (int floorIndex = 0; floorIndex < numberOfFloors; floorIndex++)
				{
					elevator.getElevatorButton()[floorIndex] = controller.getElevatorButton(
						elevatorIndex, floorIndex);
				}

				for (int floorIndex = 0; floorIndex < numberOfFloors; floorIndex++)
				{
					elevator.getServicesFloors()[floorIndex] = controller.getServicesFloors(
						elevatorIndex, floorIndex);
				}

				systemData.getElevators().add(elevator);
			}

			currentClockTick = controller.getClockTick();
			tries++;
		} while (currentClockTick != systemData.getClockTick() && tries < MAX_TRIES);

		if (currentClockTick != systemData.getClockTick())
		{
			throw new RemoteException("can not get consistent state");
		}

		return systemData;

	}

	private static DirectionType convertDirectionType(int value)
		throws InvalidEnumValueException
	{
		switch (value)
		{
			case IElevator.ELEVATOR_DIRECTION_UNCOMMITTED:
			{
				return DirectionType.UNCOMMITTED;
			}
			case IElevator.ELEVATOR_DIRECTION_DOWN:
			{
				return DirectionType.DOWN;
			}
			case IElevator.ELEVATOR_DIRECTION_UP:
			{
				return DirectionType.UP;
			}
			default:
			{
				throw new InvalidEnumValueException();
			}
		}
	}

	private static int convertFromDirectionType(DirectionType direction)
	{
		switch (direction)
		{
			case UNCOMMITTED:
			{
				return IElevator.ELEVATOR_DIRECTION_UNCOMMITTED;
			}
			case DOWN:
			{
				return IElevator.ELEVATOR_DIRECTION_DOWN;
			}
			case UP:
			{
				return IElevator.ELEVATOR_DIRECTION_UP;
			}
		}

		throw new InvalidParameterException(
			"the value of the parameter value is invalid");
	}

	private static DoorState convertDoorState(int value)
		throws InvalidEnumValueException
	{
		switch (value)
		{
			case IElevator.ELEVATOR_DOORS_OPEN:
			{
				return DoorState.OPEN;
			}
			case IElevator.ELEVATOR_DOORS_OPENING:
			{
				return DoorState.OPENING;
			}
			case IElevator.ELEVATOR_DOORS_CLOSED:
			{
				return DoorState.CLOSED;
			}
			case IElevator.ELEVATOR_DOORS_CLOSING:
			{
				return DoorState.CLOSING;
			}
			default:
			{
				throw new InvalidEnumValueException();
			}
		}
	}

	/**
	 * @param controller the controller to set
	 */
	public void setController(IElevator controller)
	{
		this.controller = controller;
	}

	@Override
	public boolean updateNewTargets(SystemData systemData,
		List<ElevatorControlData> controlElevatorList) throws RemoteException
	{
		boolean returnValue = true;
		ArrayList<ElevatorData> elevators = systemData.getElevators();
		for (int ii = 0; ii < elevators.size(); ii++)
		{
			// returnValue has to be the right condition !!!!
			// if it is on the left side updateNewTarget will not be executed in
			// the negative state
			returnValue = updateNewTarget(elevators.get(ii),
				controlElevatorList.get(ii)) && returnValue;
		}
		return returnValue;
	}

	@Override
	public boolean updateNewTarget(ElevatorData elevator,
		ElevatorControlData controlData) throws RemoteException
	{
		if (elevator.getElevatorDoorStatus() != DoorState.OPEN)
		{
			return false;
		}

		if (controlData.getNewTarget() != ElevatorControlData.NO_TARGET)
		{

			DirectionType direction = null;
			if (elevator.getElevatorFloor() == controlData.getNewTarget())
			{
				direction = DirectionType.UNCOMMITTED;
			}
			else
			{

				if (elevator.getElevatorFloor() < controlData.getNewTarget())
				{
					direction = DirectionType.UP;
				}
				else
				{
					direction = DirectionType.DOWN;
				}

			}

			if (elevator.getCurrentTarget() != controlData.getNewTarget() || elevator.getCommittedDirection() != direction)
			{
				controller.setTarget(elevator.getElevatorIndex(),
					controlData.getNewTarget());
				controller.setCommittedDirection(elevator.getElevatorIndex(),
					RemoteElevatorAdapter.convertFromDirectionType(direction));
			}
		}
		else if (controlData.getNewDirection() != null)
		{
			if (elevator.getCommittedDirection() != controlData.getNewDirection())
			{
				controller.setCommittedDirection(
					elevator.getElevatorIndex(),
					RemoteElevatorAdapter.convertFromDirectionType(controlData.getNewDirection()));
			}
		}

		return true;
	}
}
